import logging
import re, traceback

greek_alphabeta = {r'\alpha': 'alpha', r'\beta': 'beta', r'\gamma': 'gamma', r'\Gamma': 'Gamma', r'\Delta': 'Delta', \
                   r'\delta': 'delta', r'\epsilon': 'epsilon', r'\varepsilon': 'epsilon', r'\zeta': 'zeta', \
                   r'\eta': 'eta', r'\Theta': 'Theta', r'\theta': 'theta', r'\Omega': 'Omega', r'\omega': 'omega', \
                   r'\iota': 'iota', r'\kappa': 'kappa', r'\mu': 'mu', r'\nu': 'nu', r'\Xi': 'Xi', r'\xi': 'xi', \
                   r'\omicron': 'omicron', r'\Pi': 'Pi', r'\pi': 'pi', r'\pho': 'pho', r'\Sigma': 'Sigma',
                   r'\sigma': 'sigma', \
                   r'\tau': 'tau', r'\Upsilon': 'Upsilon', r'\upsilon': 'upsilon', r'\Phi': 'Phi', r'\phi': 'phi', \
                   r'\varphi': 'phi', r'\chi': 'chi', r'\Psi': 'Psi', r'\psi': 'psi'}

type_dict = {'float64': 'float64', 'float32': 'float32', 'unint32': 'unint32', 'siint32': 'siint32', \
             'struct': 'struct', 'boolu32': 'boolu32', 'polymor': 'polymor', 'siint08': 'siint08', 'unint08': 'unint08', \
             'llong64': 'llong64', 'ulong64': 'ulong64'}

latex_symbols = [r'\frac', r'\sqrt', r'\eqmulti', r'\sin', r'\cos', r'\tan', r'\arcsin', r'\arccos', r'\arctan']

binary_op = ['frac']
unary_op = ['cos', 'sin', 'tan', 'arcsin', 'arccos', 'arctan', 'sqrt', 'eqmulti']
latex2func_dict = {r'\sin': 'Sinx', r'\cos': 'Cosx', r'\tan': 'Tanx', r'\arcsin': 'Asinx', r'\arccos': 'Acosx',
                   r'\arctan': 'Atanx', r'\sqrt': 'Sqrtx'}

funcname_verb = {'初始': 'Initial', '计算': 'Calculate', '测量': 'Measure', '预估': 'Evaluate', '判断': 'Judge'}
funcname_noun = {'位置': 'Location', '姿态': 'Gesture', '角度': 'Angle', '速度': 'Speed', '矩阵': 'Matrix',
                 '数据': 'Data',
                 '特征': 'Feature', \
                 '参数': 'Parameter'}

# key words in poseudocode
if_words = ['If', 'IF', 'if']
elif_words = ['Elseif', 'ELSEIF', 'elseif']
else_words = ['Else', 'ELSE', 'else']
endif_words = ['Endif', 'ENDIF', 'endif']
for_words = ['For', 'FOR', 'for']
endfor_words = ['Endfor', 'ENDFOR', 'endfor']
while_words = ['While', 'WHILE', 'while']
endwhile_words = ['Endwhile', 'endwhile', 'ENDWHILE']
note_words = ['//', '/*']
endnote_words = ['*/']
key_words = if_words + for_words + while_words + note_words

special_symbols = ['×', '*']

logger = logging.getLogger(__name__)


def bracket_match(expr: str, idx: int):
    # given an expr and the index of a left or right bracket,
    # return the index of matched right or left bracket

    stack = [idx]
    i = idx + 1
    add = 1
    bra = expr[idx]
    tar = ''
    if bra == '{':
        tar = '}'
    elif bra == '}':
        tar = '{'
    elif bra == '(':
        tar = ')'
    elif bra == ')':
        tar = '('
    elif bra == '[':
        tar = ']'
    elif bra == ']':
        tar = '['
    if bra in ['}', ')', ']']:
        i = idx - 1
        add = -1
    while i < len(expr):
        if expr[i] == bra:
            stack.append(i)
        elif expr[i] == tar:
            stack = stack[:-1]
            if len(stack) == 0:
                return i
        i += add
    raise Exception('No bracket matched!', expr[:idx + 1])


def delete_bracket(expr: str, symb=None):
    # delete useless brackets and spaces outside 'expr'

    expr = expr.strip()
    l, r = 0, len(expr) - 1
    bra = ['(']
    tar = {'(': ')', '{': '}'}
    if symb != None and symb == '{':
        bra.append(symb)
    while l < len(expr) and expr[l] in bra:
        if expr[r] != tar[expr[l]]:
            break
        if bracket_match(expr, l) == r:
            l += 1
            r -= 1
        else:
            break
    flag = (l != 0)
    return flag, expr[l: r + 1].strip()


def expr_composite_op(expr):
    # e.g. a += b -> a = a + (b)

    res = None
    # +=, -=, *=, /=
    for symb in ['+=', '-=', '*=', '/=']:
        if (pos := expr.find(symb)) != -1:
            if if_independent_symbol(expr, pos):
                res = expr[:pos].strip() + ' = ' + expr[:pos].strip() + ' ' + symb[0] + ' (' + expr[
                                                                                               pos + 2:].strip() + ')'
                break
    return res


def expr_unary_op(expr):
    # ++, --

    res = None
    for symb in ['++', '--']:
        if (pos := expr.find(symb)) != -1:
            if if_independent_symbol(expr, pos):
                if pos == 0:
                    return pos, expr[2:].strip(), symb
                else:
                    return pos, expr[:pos].strip(), symb
    return None, None, None


def expr_eq_neq(expr):
    # return the first index of the '=' or '>' or '<' or '>=' or '<=' and the symbol
    # return -1 and '' if not found
    # order can't be changed (because '>=' contains '>' and '=')
    symbol_dict = {'==': '==', '>=': '>=', '<=': '<=', '!=': '!=', '=': '=', '>': '>', '<': '<', r'\ne': '!='}
    for symb in symbol_dict.keys():
        if (pos := expr.find(symb)) != -1:
            if expr[pos] in ['<', '>']:
                if (pos > 0 and expr[pos - 1] == expr[pos]) or (pos < len(expr) - 1 and expr[pos + 1] == expr[pos]):
                    continue
                if expr[pos] == '>' and pos > 0 and expr[pos - 1] == '-':
                    continue
            if if_independent_symbol(expr, pos):
                return pos, pos + len(symb), symbol_dict[symb]
    return -1, -1, ''


def expr_note(expr):
    for i in range(len(expr)):
        if expr[i] == '/':
            if (i < len(expr) - 1 and expr[i + 1] == expr[i]) or (i > 0 and expr[i] == expr[i - 1]):
                return 1
            if i < len(expr) - 1 and expr[i + 1] == '*':
                return 2
        return -1


def expr_add_sub(expr):
    # return the index of the first independent sub/add, return -1 if not exist

    for i in range(len(expr)):
        if expr[i] == '+' or expr[i] == '-':
            if (i < len(expr) - 1 and expr[i + 1] == expr[i]) or (i > 0 and expr[i] == expr[i - 1]):
                continue
            # case '->'
            if expr[i] == '-' and i < len(expr) - 1 and expr[i + 1] == '>':
                continue
            # case 1.0e-10
            if expr[i] == '-' and i > 0 and expr[i - 1] == 'e' and i - 1 > 0 and expr[i - 2] in '1234567890':
                continue
            if if_independent_symbol(expr, i):
                return i, i + 1, expr[i]
    return -1, -1, ''


def expr_mul_div(expr):
    # return the index of the first independent mul/div, return -1 if not exist

    for i in range(len(expr)):
        if expr[i] == '*' or expr[i] == '/' or expr[i] == '×':
            if i + 1 < len(expr) and expr[i + 1:].strip()[0] == '=':
                j = i + expr[i:].find('=')
                sym = '='
            else:
                j = i
                sym = ''
            if if_independent_symbol(expr, i):
                return i, j + 1, expr[i] + sym
    return -1, -1, ''


def expr_and_or(expr):
    symbol_dict = {'且': '&&', '或': '||', 'or': '||', 'and': '&&', '&&': '&&', '||': '||'}
    for symb in symbol_dict.keys():
        i = 0
        while (pos := expr.find(symb, i)) != -1:
            if symb == 'and' or symb == 'or':
                if if_independent_variable(expr, pos, len(symb)):
                    return pos, pos + len(symb), symbol_dict[symb]
            elif if_independent_symbol(expr, pos):
                return pos, pos + len(symb), symbol_dict[symb]
            i = pos + len(symb)
    return -1, -1, ''


def expr_pow(expr):
    for i in range(len(expr)):
        if expr[i] == '^':
            if if_independent_symbol(expr, i):
                return i
    return -1


def expr_bit_op(expr):
    symbol_dict = {'BITAND': '&', 'BITOR': '|', '<<': '<<', '>>': '>>', 'BITXOR': '^', 'BITNOT': '~'}
    for symb in symbol_dict.keys():
        i = 0
        while (pos := expr.find(symb, i)) != -1:
            if expr[pos] in ['&', '|']:
                if (pos > 0 and expr[pos - 1] == expr[pos]) or (pos < len(expr) - 1 and expr[pos + 1] == expr[pos]):
                    i = pos + 1
                    continue
            if if_independent_symbol(expr, pos):
                return pos, pos + len(symb), symbol_dict[symb]
            i = pos + len(symb)
    return -1, -1, ''


def unique_extend(list1, list2):
    for element_ in list2:
        if element_ not in list1:
            list1.append(element_)


def update_keywords(key_words_dict):
    if 'IF' in key_words_dict.keys():
        unique_extend(if_words, key_words_dict['IF'])
    if 'ELSEIF' in key_words_dict.keys():
        unique_extend(elif_words, key_words_dict['ELSEIF'])
    if 'ELSE' in key_words_dict.keys():
        unique_extend(else_words, key_words_dict['ELSE'])
    if 'ENDIF' in key_words_dict.keys():
        unique_extend(endif_words, key_words_dict['ENDIF'])
    if 'FOR' in key_words_dict.keys():
        unique_extend(for_words, key_words_dict['FOR'])
    if 'ENDFOR' in key_words_dict.keys():
        unique_extend(endfor_words, key_words_dict['ENDFOR'])
    if 'WHILE' in key_words_dict.keys():
        unique_extend(while_words, key_words_dict['WHILE'])
    if 'ENDWHILE' in key_words_dict.keys():
        unique_extend(endwhile_words, key_words_dict['ENDWHILE'])


def find_scope_pow(expr: str, idx: int):
    # case: expr[idx] is '^'

    if expr[:idx].strip()[-1] == '}':
        for i in range(idx - 1, -1, -1):
            if expr[i] == '}':
                r1 = i
                break
        l1 = bracket_match(expr, r1)
    else:
        raise Exception('No matched field for symbol ' ^ '!(The format should be {}^{})')
    if expr[idx + 1:].strip()[0] == '{':
        l2 = expr[idx + 1:].find('{') + idx + 1
        r2 = bracket_match(expr, l2)
    else:
        raise Exception('No matched field for symbol ' ^ '!(The format should be {}^{})')
    return l1 + 1, r1, l2 + 1, r2


def find_scope_op(expr: str, idx: int):
    # idx: index of an operator, which means expr[idx] is '\\'
    # return the indexes of the operand(s)

    i = idx + 1
    while expr[i].islower():
        i += 1
    operator = expr[idx + 1: i]
    if operator in unary_op:
        ch = expr[i:].strip()[0]
        if ch == '{' or ch == '(':
            l = expr[i:].find(ch) + i
            r = bracket_match(expr, l)
            return l + 1, r
        else:
            # choose the first character(not ' ') or raise an exception
            pass
    elif operator in binary_op:
        l1 = expr[i:].find('{') + i
        r1 = bracket_match(expr, l1)
        l2 = expr[r1 + 1:].find('{') + r1 + 1
        r2 = bracket_match(expr, l2)
        return l1 + 1, r1, l2 + 1, r2


def find_scope_sum(expr: str, idx: int):
    # idx: index of symbol '\sum'
    # return the indexes of the operand

    count = 0
    pos = idx
    while count < 3:
        pos += expr[pos:].find('{')
        pos = bracket_match(expr, pos)
        count += 1
    l, r = bracket_match(expr, pos) + 1, pos
    return l, r


def find_scope_func(expr: str, idx: int):
    # idx: first index of func's name
    # return the indexes of the operand

    l = expr[idx:].find('(') + idx
    r = bracket_match(expr, l)
    return l + 1, r


def find_loop_condition(expr, idx):
    # idx: index of symbol '\sum'
    # return loop variable and the lower/upper bound

    l1 = expr[idx:].find('{') + idx
    r1 = bracket_match(expr, l1)
    l2 = expr[r1:].find('{') + r1
    r2 = bracket_match(expr, l2)
    expr1 = expr[l1 + 1:r1]
    expr2 = expr[l2 + 1:r2]
    upper_bound = expr2
    var = expr1.split('=')[0]
    lower_bound = expr1.split('=')[1]
    return var, lower_bound, upper_bound


def find_independent(expr, pattern):
    # find the position of a independent pattern in expr, return -1 when not found

    pos = 0
    while pos < len(expr):
        pos = expr[pos:].find(pattern)
        if pos == -1:
            return -1
        if if_independent(expr, pos):
            return pos
        pos += len(pattern)
    return -1


def if_independent_symbol(expr, pos):
    # pos: position of a basic symbol (e.g.: +-*/) in expr
    # return if the symbol is independent
    for i in range(len(expr)):
        if expr[i] == '(' or expr[i] == '{' or expr[i] == '[':
            if i < pos and bracket_match(expr, i) > pos:
                return False
    # Case abs e.g. |a-b|
    # a naive solution, to be modified
    i = 0
    while (idx := (expr.find('|', i))) != -1:
        i = expr.find('|', idx + 1)
        if i == -1:
            return True
        if pos > idx and pos < i:
            return False
        i = i + 1
    return True


def if_independent_variable(expr, pos, l):
    # pos: position of a variable in expr
    # return if the variable is independent

    flag = True
    d = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789_'
    if pos > 0:
        if expr[pos - 1] in d:
            flag = False
    if pos + l < len(expr):
        if expr[pos + l] in d:
            flag = False
    return flag


def if_independent_latex_symbol(expr, pos, l):
    # pos: position of a latex symbol in expr
    # return if the symbol is independent

    flag = True
    d = '_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
    if pos + l < len(expr):
        if expr[pos + l] in d:
            flag = False
    return flag


def if_independent(expr, pos):
    # pos: position of a LATEX math symbol in expr
    # return if the symbol is independent or located in the scope of anothen LATEX math symbol

    for i in range(pos):
        if expr[i] == '{':
            right_idx = bracket_match(expr, i)
            if right_idx > pos:
                return False
    return True


def find_next_var(expr, pos):
    # find next var in `expr` since position `pos`

    while pos < len(expr) and expr[pos] in [' ', r'\t', r'\n']:
        pos += 1
    if pos < len(expr) and expr[pos] not in 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_':
        return -1, -1
    for i in range(len(expr), pos - 1, -1):
        s = expr[pos:i]
        if check_var_and_array(s):
            return pos, i
    return -1, -1


def find_next_expression(s, pos, op='add'):
    # find next string after s[pos]
    # s[pos] should be '+' or '-'
    # s should be a matrix-calculation expression
    # e.g. the next expression of 'A-(B + C*{D}^{T})-B' is '(B + C*{D}^{T})', when pos is 1 and s[pos] is '-'

    i = pos + 1
    op_list = ['+', '-']
    if op == 'mul':
        op_list = ['*', '/', '×']
    while i < len(s) and s[i] not in op_list:
        if s[i] in ['(', '[', '{']:
            i = bracket_match(s, i) + 1
            continue
        i += 1
    if i >= len(s):
        return -1
    return i


def find_matched_pattern(s: str, p: str):
    # 找到s中所有匹配p的位置，返回一个list

    matched_idx = []
    start = 0
    while True:
        idx = s.find(p, start)
        if idx == -1:
            break
        matched_idx.append(idx)
        start = idx + 1
    return matched_idx


def switch_sub_add(expr: str):
    # '+' to '-', '-' to '+'

    round_bracket = 0
    curly_bracket = 0
    res = ''
    for i in range(len(expr)):
        c = expr[i]
        if expr[i] == '(':
            round_bracket += 1
        elif expr[i] == ')':
            round_bracket -= 1
        elif expr[i] == '{':
            curly_bracket += 1
        elif expr[i] == '}':
            curly_bracket -= 1
        elif expr[i] == '+' and round_bracket == 0 and curly_bracket == 0:
            c = '-'
        elif expr[i] == '-' and round_bracket == 0 and curly_bracket == 0:
            c = '+'
        res += c
    return res


def switch_mul_div(expr: str):
    # '*' to '/', '/' to '*'

    round_bracket = 0
    curly_bracket = 0
    res = ''
    for i in range(len(expr)):
        c = expr[i]
        if expr[i] == '(':
            round_bracket += 1
        elif expr[i] == ')':
            round_bracket -= 1
        elif expr[i] == '{':
            curly_bracket += 1
        elif expr[i] == '}':
            curly_bracket -= 1
        elif expr[i] == '*' and round_bracket == 0 and curly_bracket == 0:
            c = '/'
        elif expr[i] == '/' and round_bracket == 0 and curly_bracket == 0:
            c = '*'
        res += c
    return res


def is_num(expr):
    if expr == '' or expr == 'f':
        return False
    if expr.count('.') > 1 or (expr.count('.') == 1 and expr[0] == '.'):
        return False
    if expr.count('-') > 1 or (expr.count('-') == 1 and expr[0] != '-'):
        return False
    for c in expr[:-1]:
        if c not in '.-0123456789':
            return False
    if expr[-1] not in 'f.-0123456789':
        return False
    return True


def strlen_cmp(a, b):
    return len(a) > len(b)


def is_int_num(expr):
    for c in expr:
        if c not in '0123456789':
            return False
    return True


def is_hex_num(expr):
    if expr == 'H':
        return False
    if expr[-1] == 'H':
        for x in expr[:-1]:
            if x not in '1234567890ABCDEF':
                return False
        return True
    elif expr[:2] == '0x' or expr[:2] == '0X':
        for x in expr[2:]:
            if x not in '1234567890ABCDEF':
                return False
        return True
    return False


def is_unit_matrix(expr):
    if expr[0] == '{' and bracket_match(expr, 0) == len(expr) - 1:
        _, expr = delete_bracket(expr, '{')
        if expr == 'I':
            return True
        pattern = r'I_\{(\d+)[x×*]\1\}'
        if re.fullmatch(pattern, expr):
            return True
    return False


def is_scientific_notation(expr: str):
    if expr.count('e') == 1:
        pre = expr.split('e')[0]
        post = expr.split('e')[1]
        if is_num(pre) and is_num(post):
            return True
    return False


def is_special_vector(expr: str, var_dict: dict):
    # if expr is a[][], a[][0], a[1:2][] etc.
    # a must be in the global variable dictionary
    flag = False
    for var in var_dict.keys():
        if expr.find(var) == 0 and expr.strip() != var and expr[len(var):].strip()[0] == '[':
            flag = True
            break
    if not flag:
        return False
    beg = 0
    while (pos := expr.find('[', beg)) != -1:
        right_pos = bracket_match(expr, pos)
        s = expr[pos + 1:right_pos]
        if s == '':
            return True
        if ':' in s:
            return True
        beg = right_pos + 1
    return False


def is_latex_matrix(expr):
    expr = expr[1:-1].strip()
    if expr.find(r'\begin{matrix}') == 0 and expr.find(r'\end{matrix}') + len(r'\end{matrix}') == len(expr):
        return True
    return False


def is_multi_eq(expr: str):
    # 处理连等
    eq_idx = []
    for i in range(len(expr)):
        if expr[i] == '=':
            if i > 0 and expr[i - 1] in ['<', '>']:
                continue
            if not if_independent_symbol(expr, i):
                continue
            if i < len(expr) - 1 and expr[i + 1] == '=':
                return eq_idx
            eq_idx.append(i)
    return eq_idx


def is_array(var: str):
    # 粗略判断是否是数组
    var = var.strip()
    if var[-1] != ']':
        return False
    if bracket_match(var, len(var) - 1) != -1:
        return True
    return False


def parse_latex_matrix(expr):
    # 返回一个二维list
    res = []
    expr = expr.strip()
    expr = expr[len(r'\begin{matrix}'):]
    expr = expr[:len(expr) - len(r'\end{matrix}')]
    vec = expr.split(r'\\')[:-1]
    if vec == []:
        vec = [expr]
    for line in vec:
        line = line.strip()
        vars = line.split('&')
        vars = [var.strip() for var in vars]
        res.append(vars)
    return res


def standard_hex_num(expr):
    if expr[-1] == 'H':
        return '0x' + expr[:-1]
    elif expr[:2] == '0x' or expr[:2] == '0X':
        return '0x' + expr[2:]


def make_condition(cond):
    # cond is the judge condition in piecewise function
    # sometimes cond are not standard, this function return a standard condition in C style

    cond = cond.strip()
    symbs = ['<=', '>=', '<', '>']
    cnt = 0
    for symb in symbs[:2]:
        cnt += cond.count(symb)
    s = cond
    while s != '' and (idx := s.find('<')) != -1:
        if s[idx + 1] == '=':
            s = s[idx + 2:]
        else:
            cnt += 1
            s = s[idx + 1:]

    return cond


def check_name_validity(var):
    # check if var is a valid c variable name
    # return a fixed name if not valid

    new_var = ''
    for i in range(len(var)):
        if var[i] in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
            new_var += var[i]
        elif var[i] in [' ', '_']:
            if new_var == '':
                new_var += '_'
            elif new_var[-1] != '_':
                new_var += '_'
        elif new_var != '' and new_var[-1] != '_':
            new_var += '_'
    if new_var == '':
        return ''
    if new_var[0] in '0123456789':
        new_var = '_' + new_var
    if new_var.count('_') == len(new_var):
        new_var = ''
    return new_var


def check_var_validity(var):
    # check if var is a valid c variable name
    # return a fixed name if not valid

    if var[0] not in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
        return False
    for i in range(len(var)):
        if var[i] not in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321':
            return False
    return True


def check_var_and_array(s):
    # check if s is a valid c variable name or if s is a valid c-array, struct and class instance considered.
    # e.g. a[i][j].b[k][l]

    if check_var_validity(s):
        return True
    s_var = r'[A-Za-z_][0-9A-Za-z_]*'
    s_num = r'[1-9][0-9]*'
    s_array = fr'({s_var})(\[({s_var}|{s_num})\])*'
    s_array_plus = fr'{s_array}((\.|->)({s_array}))*'
    pattern = re.compile(s_array_plus)
    reg = pattern.search(s)
    if reg:
        s_ = reg.group()
        if s_ == s:
            return True
    return False


def drop_square_brackets(expr: str):
    # e.g. a[][i] -> a

    expr = expr.strip()
    while expr[-1] == ']':
        i = bracket_match(expr, len(expr) - 1)
        expr = expr[:i].strip()
    return expr


def get_unit_matrix_dim(s: str):
    res = ''
    for i in range(len(s)):
        while s[i] in '0987654321':
            res += s[i]
            i = i + 1
        if res != '':
            return int(res)
    return None


def get_arrayElement_dim(var: str, s: str, var_dim: str):
    # var: variable name. already in the data-dictionary
    # s: the string which starts with `var`, and the first char after var is '['. e.g. a[10][10]
    # dim: dimension. e.g. '3*1'

    s = s[len(var):].strip()
    count = 0
    while s != '' and s[0] == '[':
        r = bracket_match(s, 0)
        if r == -1:
            return None
        s = s[r + 1:]
        count += 1
    if s != '':
        return None
    var_dim = var_dim.split('*')
    if '1' in var_dim and len(var_dim) == 2:
        return '1*1'
    else:
        if count == 1:
            if len(var_dim) == 2:
                return f'1*{var_dim[1]}'
            else:
                return '*'.join(var_dim[count:])
        else:
            if len(var_dim) <= count:
                return '1*1'
            elif len(var_dim) - 1 == count:
                return f'1*{var_dim[-1]}'
            else:
                return '*'.join(var_dim[count:])


def detect_op(expr):
    # expr -> str: an expr which not contains basic operators
    # detecting the the first function or latex symbol in expr
    # return the detected operator and corresponding index

    # pattern_func = re.compile(r'[A-Za-z_]\w*\s*\(\w*\s*\w*\s*(\*)*\w+\s*(,\s*\w*\s*\w*\s*(\*)*\w+\s*)*\)')
    pattern_func = re.compile(r'[A-Za-z_][\w\.\[\]]*[ \t]*\(.+(,.+)*\)')
    pattern_latex = re.compile(r'\\[A-Za-z]+\s*[\{\(]')
    reg1 = pattern_func.search(expr)
    reg2 = pattern_latex.search(expr)
    # the first detected must not be in another operator's scope
    if expr[0] == '[' and expr[-1] == ']' and bracket_match(expr, 0) == len(expr) - 1:
        return expr, 0
    if expr[0] == '|' and expr[-1] == '|':
        return expr, 0
    if reg1 == None and reg2 == None:
        return expr, -1
    elif reg1 == None:
        op = expr[reg2.span()[0]:reg2.span()[1] - 1].strip()
        if op in latex_symbols:
            return op, reg2.span()[0]
        else:
            erro_print(traceback.extract_stack(), 'No matched operator: {}'.format(op))
            return None, -1
    elif reg2 == None:
        op = reg1.group().split('(')[0].strip()
        return op, reg1.span()[0]
    else:
        if reg1.span()[0] < reg2.span()[0]:
            return reg1.group().split('(')[0].strip(), reg1.span()[0]
        else:
            return expr[reg2.span()[0]:reg2.span()[1] - 1].strip(), reg2.span()[0]


def get_var_type(name, var_dict):
    dims = var_dict[name]['dim'].split('*')
    ty = var_dict[name]['type']
    if ty not in type_dict.keys():
        # erro_print(traceback.extract_stack(), f'Type {ty} not support!')
        type_dict[ty] = ty
    # if dims == '' or ty == '':
    #     erro_print(traceback.extract_stack(), f'No dim or type information in the DATA-DICTION! Varable is {name}')
    if len(set(dims)) == 1 and list(set(dims))[0] == '1':
        return type_dict[ty], [1]
    else:
        dims = [int(x) for x in dims]
        if len(dims) == 2 and 1 in dims:
            dims.remove(1)
            if dims == []:
                dims = [1]
        return type_dict[ty], dims


def find_function_args(expr):
    # expr: contents of argument list of a given function
    # renturn a list of the arguments

    args = []
    i = 0
    s = 0
    while ((pos := expr.find(',', i)) != -1):
        if if_independent_symbol(expr, pos):
            args.append(expr[s:pos].strip())
            s = pos + 1
        i = pos + 1
    args.append(expr[s:].strip())
    return args


def erro_print(exc_traceback, erro_info, debug=True, llm_mode=False):
    erro_info = 'Exception: ' + erro_info
    if debug:
        erro_info += '\nTraceback (most recent call last):'
        for frame_info in exc_traceback:
            frame, lineno, module, line = frame_info
            erro_info += '\n  File "{}", line {}, in {}\n    {}'.format(frame, lineno, module, line)
    logger.error(erro_info)
    if not llm_mode:
        raise Exception


def symbol_replace(expr, sym, new_sym):
    # check if sym is independent when sym is a latex symbol start with '\'
    if sym[0] != r'\\':
        return expr.replace(sym, new_sym)
    beg = 0
    s = expr
    while (pos := s[beg:].find(sym)) != -1:
        if if_independent_latex_symbol(s, pos + beg, len(sym)):
            s = s[:pos + beg] + s[pos + beg:].replace(sym, new_sym, 1)
            beg = beg + pos + len(new_sym)
        else:
            beg = beg + len(sym)
    return s


def key_word_replace(expr, key_words_list, target_key_word):
    # 关键词替换
    last_pos = 0
    new_expr = ""
    for key_word in key_words_list:
        if key_word == target_key_word:
            continue
        matched_idx = find_matched_pattern(expr, key_word)
        for idx in matched_idx:
            post_num_flag = False
            if idx + len(key_word) < len(expr) and expr[idx + len(key_word)] in "1234567890":
                post_num_flag = True
            len_word = len(key_word)
            if post_num_flag:
                len_word += 1
            # 需要是独立的，防止诸如if出现在其他变量名里这种可能
            if if_independent_variable(expr, idx, len_word):
                new_expr += expr[last_pos:idx]
                new_expr += target_key_word
                if post_num_flag:
                    new_expr += expr[idx + len(key_word)]
                last_pos = idx + len_word
        new_expr += expr[last_pos:]
        expr = new_expr
        new_expr = ""
        last_pos = 0
    return expr


def find_independent_symbol(expr, symb):
    # return the position of symb in expr, -1 if no symb found
    pos = 0
    while (pos := expr.find(symb, pos)) != -1 and pos < len(expr):
        if if_independent_symbol(expr, pos):
            return pos
        pos += 1
    return -1


def generate_skew_symmetric_matrix_code(mat, vec):
    # generate code segment for vector `vec`
    code_lines = ''
    code_lines += f'{mat}[0][0] = 0;\n'
    code_lines += f'{mat}[0][1] = -{vec}[2];\n'
    code_lines += f'{mat}[0][2] = {vec}[1];\n'
    code_lines += f'{mat}[1][0] = {vec}[2];\n'
    code_lines += f'{mat}[1][1] = 0;\n'
    code_lines += f'{mat}[1][2] = -{vec}[0];\n'
    code_lines += f'{mat}[2][0] = -{vec}[1];\n'
    code_lines += f'{mat}[2][1] = {vec}[0];\n'
    code_lines += f'{mat}[2][2] = 0;'
    return code_lines


def get_bit_from_var(var, bit):
    code = ''
    if (pos := bit.find(':')) != -1:
        low = bit[:pos].strip()
        high = bit[pos + 1:].strip()
        if low[0] != 'D' or high[0] != 'D':
            return None
        if (not is_int_num(low[1:])) or (not is_int_num(high[1:])):
            return None
        beg = low[1:].lstrip('0')
        if beg == '':
            beg = '0'
        beg = int(beg)
        end = high[1:].lstrip('0')
        if end == '':
            end = '0'
        end = int(end)
        if beg > end:
            beg, end = end, beg
        n = end - beg
        code = f'UI32_BIT_GETN({var}, {str(beg)}, {str(n)})'
        return code
    else:
        if bit[0] != 'D':
            return None
        num = bit[1:].strip().lstrip('0')
        if num == '':
            num = '0'
        if not is_int_num(num):
            return None
        code = f'UI32_BIT_GET1({var}, {num})'
        return code


def get_args_type(func_signature: str) -> list[str]:
    res = []
    l_bra = func_signature.find('(')
    if l_bra == -1:
        return None
    r_bra = bracket_match(func_signature, l_bra)
    args = func_signature[l_bra + 1:r_bra].split(',')
    for arg in args:
        ty = arg.split()[0].strip()
        if ty == 'const':
            ty = arg.split()[1].strip()
            if arg.split()[2].strip()[0] == '*':
                res.append(ty + '*')
            else:
                res.append(ty)
        else:
            if arg.split()[1].strip()[0] == '*':
                res.append(ty + '*')
            else:
                res.append(ty)
    return res


def loop_condition_transform(expr: str):
    pattern1 = re.compile(r'([^:~]+)[ \t]*=[ \t]*([^:~]+)[:~-]([^:~]+)')
    pattern2 = re.compile(r'([^;]*)\s*;\s*([^;]*)\s*;\s*([^;]*)')
    match1 = pattern1.search(expr)
    cond = None
    if match1:
        var = match1.group(1)
        _start = match1.group(2)
        _end = match1.group(3)
        return f'{var}={_start}', f'{var}<={_end}', f'{var}++'
    else:
        match2 = pattern2.search(expr)
        if match2:
            return match2.group(1), match2.group(2), match2.group(3)
        else:
            return None, None, None


def parser_decorator(func):
    def wrapper(self, *args, **kwargs):
        if len(args) != 0:
            node = args[0]
        else:
            node = kwargs['node']
        try:
            func(self, *args, **kwargs)
        except Exception as e:
            logger.error('--------------------------------------------------')
            if node.lines != None:
                logger.error(f'Wrong! From line {node.lines[0]} to line {node.lines[1]}')

            fa = node.father
            son = node
            while fa.op != 'body':
                son = fa
                fa = fa.father
            logger.error(f'An exception is raised when parsing "{node.expr}" in sentence "{son.expr}"')
            if str(e).strip():
                logger.error(e)
            if self.data.llm_mode:
                node.fail_to_generate()
            raise e

    return wrapper


def independent_split(expr: str, sep: str):
    # 支持只按独立的sep分割，并对分割后的每个字符串strip掉多余的空白字符
    # 例："func(a,b),c" -> ["func(a,b)", "c"]

    l = idx = 0
    result = []
    while (pos := expr.find(sep, idx)) != -1:
        if if_independent_symbol(expr, pos):
            result.append(expr[l:pos].strip())
            l = idx = pos + len(sep)
        else:
            idx = idx + 1
    if expr[l:].strip() != '':
        result.append(expr[l:].strip())
    return result


if __name__ == '__main__':
    # 测试从函数签名获取类型列表
    s = "float64 min(float64 * a, const float64 * b, float * c)"
    print(get_args_type(s))
    print()
    # 测试latex矩阵解析
    s = r'- (anb1[123][dsdsa]._sada1[sada][sdad]->adsadas[1212121] + 1)'
    a, b = find_next_var(s, 1)
    print(s[a:b])

    latex_matrix = r'\begin{matrix}1&0&0&0&0&0\\0&1&0&0&0&0\\0&0&1&0&0&0\\\end{matrix}'

    mat = parse_latex_matrix(latex_matrix)
    print(mat)
    # 测试if关键词替换
    s = r'''
IF1(BZ1==1且AscendFlg==1且HoverFlg==0且PSOOutFlg==0)
PSOOutFlg=1;
Endif1
If1()
if1(BZ1==1且PSOOutFlg==1且PEGInitFlg==0且IAFlg==0)
调用4.6.7(垂直上升终端状态asdasdasd预测);
Tmpr=r_{_I };
Tmpv=v_{_I };
r_{_I}=r_{pf };
v_{_I}=v_{pf };
调用4.6.5(PEG初始化);
r_{_I}=Tmpr ;
v_{_I}=Tmpv ;
PEGInitFlg=1 ;
ENDIF1
endif1
If1(BZ1==1且PEGInitFlg==1)
a;//调用4.6.8(上升制导参数规划)
ENDIF1
'''

    s = key_word_replace(s, if_words, "IF")
    s = key_word_replace(s, endif_words, "ENDIF")
    s = key_word_replace(s, elif_words, "ELSEIF")
    s = key_word_replace(s, else_words, "ELSE")
    print(s)

